#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_freertos_hooks.h"
#include "esp_task_wdt.h"
#include "freertos/queue.h"

#include "led_74hc595.h"
#include "system.h"
#include "led.h"
#include "ec11.h"

/**
 * **********************************************************
 * 任务句柄 & 任务函数声明 & 队列句柄
 * **********************************************************
 */
TaskHandle_t show_task_handle = NULL;
TaskHandle_t key_task_handle = NULL;
TaskHandle_t ec11_task_handle = NULL;
void show_task(void *pvParam);
void key_task(void *pvParam);
void ec11_task(void *pvParam);

QueueHandle_t gpio_event_queue = NULL;

portMUX_TYPE mmux = portMUX_INITIALIZER_UNLOCKED;

void tick_callback_hook(void); /* 钩子函数声明 */

/**
 * **********************************************************
 * EC11 按键中断服务函数
 * **********************************************************
 */
static void IRAM_ATTR ec11_btn_isr_handle(void *arg)
{
    uint32_t io_num = (uint32_t)arg;
    xQueueSendFromISR(gpio_event_queue, &io_num, NULL);
    vTaskNotifyGiveFromISR(key_task_handle, NULL);
}

/**
 * **********************************************************
 * app_main() 主函数
 * **********************************************************
 */
void app_main(void)
{
    /**
     * 初始化
     */
    xtp_system_init(&xtp_system);
    ec11.init();
    hc595_init();

    /**
     * 创建任务
     */
    xTaskCreate(show_task, "show_task", 1024 * 8, NULL, 1, &show_task_handle);
    xTaskCreate(key_task, "key_task", 1024 * 8, NULL, 2, &key_task_handle);
    xTaskCreate(ec11_task, "ec11_task", 1024 * 8, NULL, 1, &ec11_task_handle);

    if (show_task_handle != NULL)
    {
        ESP_LOGI("example", "show_task 任务创建成功");
    }

    if (key_task_handle != NULL)
    {
        ESP_LOGI("example", "key_task 任务创建成功");
    }

    if (ec11_task_handle != NULL)
    {
        ESP_LOGI("example", "ec11_task 任务创建成功");
    }

    /**
     * 注册钩子函数
     */
    esp_err_t state;
    state = esp_register_freertos_tick_hook(tick_callback_hook);
    if (state == ESP_OK)
    {
        ESP_LOGI("example", "tick_callback_hook() 钩子注册成功");
    }
    else
    {
        ESP_LOGI("example", "tick_callback_hook() 钩子注册失败");
    }

    /**
     * 创建队列
     */
    gpio_event_queue = xQueueCreate(10, sizeof(uint32_t));

    gpio_install_isr_service(0);
    gpio_isr_handler_add(EC11_BTN_GPIO, ec11_btn_isr_handle, (void *)EC11_BTN_GPIO);

    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

/**
 * **********************************************************
 * show_task() 显示任务函数
 * **********************************************************
 */
void show_task(void *pvParam)
{
    while (1)
    {
        ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
        
        taskENTER_CRITICAL(&mmux);
        hc74595_display(xtp_system.test_num);
        taskEXIT_CRITICAL(&mmux);
    }

    vTaskDelete(NULL);
}

void key_task(void *pvParam)
{
    uint32_t io_num = 0;
    uint32_t backup_time = 0;
    bool press_key = false;
    bool lift_key = false;
    while (1)
    {
        /* 接收按键中断发来任务通知 */
        ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
        
        /* 从 gpio_event_queue 队列中接收数据 */
        xQueueReceive(gpio_event_queue, &io_num, portMAX_DELAY);

        /* 获取按下并记录按下的时间 */
        if (gpio_get_level(io_num) == 0)
        {
            press_key = true;
            backup_time = xTaskGetTickCount();
        }
        else
        {
            lift_key = true;
            backup_time = xTaskGetTickCount() - backup_time;
        }

        if (press_key & lift_key)
        {
            press_key = false;
            lift_key = false;

            if (backup_time > 1000)
            {
                // 长按
                xtp_system.test_num = 0;
                xTaskNotifyGive(show_task_handle);
            }
            else
            {
                // 短按
                xtp_system.test_num++;
                xTaskNotifyGive(show_task_handle);
            }
        }
    }
}

/**
 * **********************************************************
 * EC11 旋转编码器任务
 * **********************************************************
 */
void ec11_task(void *pvParam)
{

    while (1)
    {

        vTaskDelay(1000);
    }
}

/**
 * **********************************************************
 * Tick 钩子函数 1ms 执行一次
 * **********************************************************
 */
void tick_callback_hook(void)
{
    xtp_system.StateLed.state_led_count++;
    xtp_system.StateLed.state_led();
}
